<template>
  <el-form
    ref="form"
    :model="model"
    :class="formClass"
    :style="formStyle"
    label-position="right"
    label-width="auto"
    require-asterisk-position="left"
    v-bind="options.attributes ? options.attributes : {}"
    v-on="options.events ? options.events : {}"
  >
    <slot>
      <template v-for="item of options.formItems">
        <slot
          :name="'formItem-' + item.prop"
          :model="model"
          :formItem="item"
        >
          <el-form-item
            v-if="formItemShow(item)"
            :key="item.prop"
            :prop="item.prop"
            :class="formItemClass(item)"
            :style="formItemStyle(item)"
            :label="formItemLabel(item)"
            :rules="formItemRule(item)"
            @keyup.enter="onFormItemEnter(item)"
            v-bind="item.attributes ? item.attributes : {}"
            v-on="item.events ? item.events : {}"
          >
            <template v-slot:label="{ label }">
              <slot
                :name="'formItemLabel-' + item.prop"
                :model="model"
                :formItem="item"
              >
                {{ label }}
                <template v-if="item.tip">
                  <ArkTooltip
                    v-if="typeof item.tip === 'string'"
                    class="h-full"
                    :content="item.tip"
                    effect="dark"
                  />

                  <ArkTooltip
                    v-else
                    class="h-full"
                    :title="item.tip.title"
                    :content="item.tip.content"
                    :type="item.tip.type"
                    effect="dark"
                    v-bind="item.tip.attributes !== undefined? item.tip.attributes : {}"
                    v-on="item.tip.events !== undefined ? item.tip.events : {}"
                  />
                </template>
                <span v-if="options.delimiter">{{ options.delimiter }}</span>
              </slot>
            </template>

            <slot
              :name="'formItemInteraction-' + item.prop"
              :model="model"
              :formItem="item"
            >
              <ArkFormInteraction
                :ref="(el) => {
                    formItemInteractionRefs[item.prop] = el;
                  }"
                :form-item="item"
                v-model="model"
                :style="formItemInteractionStyle(item)"
              />
            </slot>
          </el-form-item>
        </slot>
      </template>
    </slot>
  </el-form>
</template>

<script setup lang="ts">
import {computed, onBeforeMount, onBeforeUnmount, type PropType, ref, useTemplateRef, watch,} from "vue";
import _ from "lodash";
import ArkFormOptionsDefault, {
  ArkFormExpose,
  ArkFormInteractionExpose,
  ArkFormOptions,
  FormItem,
  LayoutMode
} from "./ArkFormOptions";
import ArkFormInteraction from "./ArkFormInteraction.vue";
import ArkTooltip from "../../basic/ark-tooltip/ArkTooltip.vue";

/** 接口（组件的接口）。包括：defineOptions、defineModel、defineProps、defineEmits、defineExpose、defineSlots */
const options = defineModel("options", {
  type: Object as PropType<ArkFormOptions>,
  default: () => ArkFormOptionsDefault
});

defineExpose<ArkFormExpose>({
  validate() {
    return formRef.value?.validate();
  },
  reset() {
    formRef.value?.resetFields();
  },
  fetchOptions(formItem, formItems) {
    const index = _.findKey(formItems, (value) => {
      return value === formItem;
    });

    if (_.isNil(index)) {
      return Promise.resolve();
    }

    const key = formItem.prop
    return formItemInteractionRefs.value[key]?.fetchOptions();
  },
});

/** 本地状态 (本地的响应式 property)。包括：ref、computed */
const model = ref({});

const formRef = useTemplateRef("form");

type FormItemInteractionRefs = {
  [propName: string]: ArkFormInteractionExpose;
};
const formItemInteractionRefs = ref<FormItemInteractionRefs>({} as FormItemInteractionRefs);

const isGridLayoutMode = computed(() => {
  return options.value.layout.mode === LayoutMode.GRID;
});
const formClass = computed(() => {
  let result: string[] = [];

  // 布局
  if (isGridLayoutMode.value) {
    result.push("grid");

    const column = options.value.layout?.column;
    const visionTemplate = options.value.layout?.visionTemplate;
    if (!visionTemplate) {
      result.push(`grid-cols-${column}`);
    }
  } else {
    result = result.concat(["flex", "flex-wrap"]);
  }

  // 间距
  const gapX = options.value.layout.gapX;
  if (gapX) {
    result = result.concat(`gap-x-${gapX}`);
  }
  const gapY = options.value.layout.gapY;
  if (gapY) {
    result = result.concat(`gap-y-${gapY}`);
  }

  return result;
});
const formStyle = computed(() => {
  if (isGridLayoutMode.value) {
    const visionTemplate = options.value.layout?.visionTemplate;
    if (visionTemplate) {
      return {
        "grid-template-areas": visionTemplate,
      };
    }
  }
  return {};
});

/** 事件（通过响应式事件触发的回调）。包括：watch */
// 内部 model、外部 options model、options formItem 需要同步
// 1️⃣内部 model 变化时同步 options model 及 formItem value
watch(
  model.value,
  (newVal, oldVal) => {
    options.value.formItems.forEach((item) => {
      item.value = model.value[item.prop];
    });
    options.value.model = model.value;
  },
  {
    deep: true,
  },
);
// 2️⃣options model 变化时同步 options formItem 及内部 model
watch(
  () => {
    return options.value.model;
  },
  (newVal, oldVal) => {
    _.forOwn(options.value.model, (value, key) => {
      model.value[key] = value;
    });

    options.value.formItems.forEach((item) => {
      item.value = model.value[item.prop];
    });
  },
);
// 3️⃣options formItem 每项 value 变化时同步 options model 及内部 model
watch(
  () => {
    return options.value.formItems;
  },
  (newVal, oldVal) => {
    // 新值跟旧值的value不全一样时才同步model，因为可能会有嵌套修改
    const newValObj = {};
    newVal.forEach((item) => {
      newValObj[item.prop] = item.value;
    });

    const oldValObj = {};
    oldVal.forEach((item) => {
      oldValObj[item.prop] = item.value;
    });

    const keys = Object.keys(oldValObj);
    let diff = false;
    keys.forEach((key) => {
      if (oldValObj[key] !== newValObj[key]) {
        diff = true;
      }
    });

    if (!diff) {
      return;
    }

    options.value.formItems.forEach((item) => {
      model.value[item.prop] = item.value;
    });
    options.value.model = model.value;
  },
  {
    deep: true,
  },
);

/** 生命周期钩子（按照它们被调用的顺序）。包括：onBeforeMount、onMounted、onBeforeUpdate、onUpdated、onBeforeUnmount、onUnmounted */
onBeforeMount(() => {
  init();
});

onBeforeUnmount(() => {
  // 重置表单
  formRef.value?.resetFields();

  // 重置同步
  options.value.model = model.value;
  options.value.formItems.forEach((item) => {
    item.value = model.value[item.prop];
  });
});

/** 非响应式的 property (不依赖响应系统的实例 property)。例如：常规函数 */
const init = () => {
  _.defaultsDeep(options.value, ArkFormOptionsDefault);

  // 状态同步。优先级从高到低依次为options.model、options.formItems.value、内部model
  if (!_.isEmpty(options.value.model)) { // options.model非空则优先，将options.model同步至options.formItems.value
    options.value.formItems.forEach((item) => {
      item.value = options.value.model[item.prop];
    });
  } else { // 否则，将options.formItems.value同步至options.model
    options.value.formItems.forEach((item) => {
      options.value.model[item.prop] = _.isNil(item.value) ? null : item.value;
    });
  }

  // 无论如何，需要与内部model同步
  options.value.formItems.forEach((item) => {
    model.value[item.prop] = _.isNil(item.value) ? null : item.value;
  });
};

const formItemShow = (formItem: FormItem) => {
  if (_.isNil(formItem.show)) {
    return true;
  }

  if (typeof formItem.show === "function") {
    return formItem.show(model.value);
  }
  return formItem.show;
};
const formItemClass = (formItem: FormItem) => {
  const result = ["!mb-0"];

  if (isGridLayoutMode.value) {
    const visionTemplate = options.value.layout?.visionTemplate;
    if (!visionTemplate) {
      if (formItem.span) {
        result.push(`col-span-${formItem.span}`);
      } else {
        result.push(`col-auto`);
      }
    }
  }
  return result;
};
const formItemStyle = (formItem: FormItem) => {
  if (isGridLayoutMode.value) {
    const visionTemplate = options.value.layout.visionTemplate;
    if (visionTemplate) {
      return {
        gridArea: formItem.prop,
      };
    }
  }
  return {};
};
const formItemLabel = (formItem: FormItem) => {
  if (typeof formItem.label === "function") {
    return formItem.label(model.value);
  }
  return formItem.label;
};
const formItemRule = (formItem: FormItem) => {
  if (typeof formItem.rule === "function") {
    return formItem.rule(model.value);
  }
  return formItem.rule;
};

const formItemInteractionStyle = (formItem: FormItem) => {
  const width = formItem.interaction?.width;

  if (typeof width === "number") {
    return {
      width: `${width}px`,
    };
  }
  if (typeof width === "string") {
    return {
      width: width,
    };
  }
  return {};
};

const onFormItemEnter = (item: FormItem) => {
  if (options.value.onFormItemEnter) {
    options.value.onFormItemEnter(item);
  }
};

/** 其它 */
</script>

<style scoped lang="scss">
:deep(.el-form-item__label) {
  align-items: center;
  height: 100%;
}

:deep(.el-form-item__label-wrap) {
  align-items: center;
  height: 100%;
}

:deep(.form-field) {
  margin-bottom: 0;
}

/*
// @import "../../lib/tailwindcss/index.css";

// TODO 待tailwindcss4实现safelist功能时可去除
.dynamic-class {
  @apply grid-cols-1 grid-cols-2 grid-cols-3 grid-cols-4 grid-cols-5 grid-cols-6 grid-cols-7 grid-cols-8 grid-cols-9 grid-cols-10 grid-cols-11 grid-cols-12;
  @apply gap-x-0 gap-x-0.5 gap-x-1  gap-x-1.5 gap-x-2 gap-x-2.5 gap-x-3 gap-x-3.5 gap-x-4 gap-x-5 gap-x-6 gap-x-7 gap-x-9 gap-x-10 gap-x-11;
  @apply gap-y-0 gap-y-0.5 gap-y-1  gap-y-1.5 gap-y-2 gap-y-2.5 gap-y-3 gap-y-3.5 gap-y-4 gap-y-5 gap-y-6 gap-y-7 gap-y-9 gap-y-10 gap-y-11;
  @apply col-span-1 col-span-2 col-span-3 col-span-4 col-span-5 col-span-6 col-span-7 col-span-8 col-span-9 col-span-10 col-span-11 col-span-12 col-span-full col-auto;
}
*/
</style>
